#include "AlgoEvaluatorDB.h"
#include "AlgoEvaluatorModuleBase.h"
#include "AlgoEvaluatorModuleFactory.h"
#include "reader/EvaluatorDataReaderFactory.h"
#include "reader/EvaluatorDataReaderBase.h"
#include <set>

class algo::AlgoEvaluatorDBPrivate
{
public:
	std::map<algobase::EvaluateEnvirDataType, std::map<std::string, darr::DimsArray<float>>> mDataMap;
};

algo::AlgoEvaluatorDB::AlgoEvaluatorDB()
	: d(new algo::AlgoEvaluatorDBPrivate)
{
}

algo::AlgoEvaluatorDB::AlgoEvaluatorDB(
	const algobase::TaskConfig& config,
	const std::vector<algobase::EvaluateL2IndexType>& types)
	: AlgoEvaluatorDB()
{
	fillData(config, types);
}

algo::AlgoEvaluatorDB::~AlgoEvaluatorDB()
{
	delete d;
}

bool algo::AlgoEvaluatorDB::fillData(
	const algobase::TaskConfig& config, 
	const std::vector<algobase::EvaluateL2IndexType>& types)
{
	std::set<algobase::EvaluateEnvirDataType> ReadTypes;
	for (const auto& type : types)
	{
		auto module = algo::AlgoEvaluatorModuleFactory::Single()->create(type);
		if (!module) {
			continue;
		}
		auto needTypes = module->getNeedType();
		for (const auto& needType : needTypes) {
			ReadTypes.insert(needType);
		}
	}

	for (const auto& loop_type : ReadTypes) 
	{
		auto loop_reader = reader::EvaEnvDataReaderFactory::Single()->create(loop_type);
		if (!loop_reader) {
			continue;
		}

		loop_reader->setTaskConfig(config);
		for (auto& pair : loop_reader->readData()) {
			d->mDataMap[pair.first] = std::move(pair.second);
		}
	}

	return true;
}

void algo::AlgoEvaluatorDB::appendData(algobase::EvaluateEnvirDataType type, std::pair<std::string, darr::DimsArray<float>>& data)
{
	d->mDataMap[type][data.first] = data.second;
}

void algo::AlgoEvaluatorDB::appendData(algobase::EvaluateEnvirDataType type, std::map<std::string, darr::DimsArray<float>>& muti_data)
{
	for (std::pair<std::string, darr::DimsArray<float>> loop_pair : muti_data) 
	{
		appendData(type, loop_pair);
	}
}

float algo::AlgoEvaluatorDB::getData(
	algobase::EvaluateEnvirDataType type, 
	const algobase::Date& date, 
	const algobase::CoordZ& coordZ, 
	float defaultValue)
{
	if (!d->mDataMap.count(type)) {
		return defaultValue;
	}

	auto dateStr = date.to_string();
	if (!d->mDataMap[type].count(dateStr)) {
		return defaultValue;
	}

	darr::DimsArray<float>& data = d->mDataMap[type][dateStr];
	if (data.isEmpty()) {
		return defaultValue;
	}

	std::map<std::string, double> map;
	map[DIM_LON_NAME] = coordZ[0];
	map[DIM_LAT_NAME] = coordZ[1];
	if (data.hasDimension(DIM_DEPTH_NAME)) {	
		map[DIM_DEPTH_NAME] = coordZ[2];
	}

	return data.at(map);
}
